package aosp.boot;

import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class BootimageUnpackerMain {
	static final String IMAGE_HOME = "D:\\vmdataspace\\images";
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		try {
			new BootimageUnpackerMain().dumpBootImage("radio");
			//new BootimageUnpackerMain().dumpBootImage("recovery-clockwork-2.5.0.7-bravo");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}
	
	public void dumpBootImage(String imageName) throws IOException {
		final FileInputStream in = new FileInputStream(IMAGE_HOME+"\\"+imageName+".img");
		
		final DataInputStream dis = new DataInputStream(in);
		
		byte[] magic = new byte[8];
		dis.readFully(magic);
		
		System.out.println("magic: "+new String(magic));
		
		int kernel_size = readUnsigned(dis);
		int kernel_addr = readUnsigned(dis);
		
		System.out.println("kernel_size: "+kernel_size);
		
		int ramdisk_size = readUnsigned(dis);
		int ramdisk_addr = readUnsigned(dis);
		
		System.out.println("ramdisk_size: "+ramdisk_size);
		
		int second_size = readUnsigned(dis);
		int second_addr = readUnsigned(dis);
		
		System.out.println("second_size: "+second_size);
		
		int tags_addr = readUnsigned(dis);
		int page_size = readUnsigned(dis);
		
		System.out.println("page_size: "+page_size);
		
		int unused1 = readUnsigned(dis);
		int unused2 = readUnsigned(dis);
		
		byte[] name = new byte[16];
		dis.readFully(name);
		
		System.out.println("name: "+new String(name));
		
		byte[] cmdline = new byte[512];
		dis.readFully(cmdline);
		
		System.out.println("cmdline: "+new String(cmdline));
		
		for (int i=0; i<8; i++)
		{
			int id = readUnsigned(dis);
			
			System.out.println("id["+i+"]: "+id);
		}
		
		/*
		** +-----------------+ 
		** | boot header     | 1 page
		** +-----------------+
		** | kernel          | n pages  
		** +-----------------+
		** | ramdisk         | m pages  
		** +-----------------+
		** | second stage    | o pages
		** +-----------------+
		**
		** n = (kernel_size + page_size - 1) / page_size
		** m = (ramdisk_size + page_size - 1) / page_size
		** o = (second_size + page_size - 1) / page_size
		**
		** 0. all entities are page_size aligned in flash
		** 1. kernel and ramdisk are required (size != 0)
		** 2. second is optional (second_size == 0 -> no second)
		** 3. load each element (kernel, ramdisk, second) at
		**    the specified physical address (kernel_addr, etc)
		** 4. prepare tags at tag_addr.  kernel_args[] is
		**    appended to the kernel commandline in the tags.
		** 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
		** 6. if second_size != 0: jump to second_addr
		**    else: jump to kernel_addr
		*/
		dis.skipBytes(page_size - 608);
		
		int n = (kernel_size + page_size - 1) / page_size;
		int m = (ramdisk_size + page_size - 1) / page_size;
		int o = (second_size + page_size - 1) / page_size;
		
		System.out.println("kernel page: "+n);
		System.out.println("ramdisk page: "+m);
		
		// kernel: page 1 <- n pages
		// page_size -> (n+1)*page_size
		dumpPart(imageName+"-kernel", dis, n*page_size);
		
		// ramdisk: page (n+1) <- m pages
		// (n+1)*page_size -> (m+n+1)*page_size
		dumpPart(imageName+"-ramdisk", dis, m*page_size);
		
		dis.close();
		in.close();
	}
	
	int readUnsigned(DataInputStream dis) throws IOException {
		int n = dis.readUnsignedByte()|(dis.readUnsignedByte()<<8)|(dis.readUnsignedByte()<<16)|(dis.readUnsignedByte()<<24);
		
		//System.out.println("*** "+n+": 0x"+Integer.toHexString(n));
		
		return n;
	}
	
	void dumpPart(String name, DataInputStream dis, int size) throws IOException {
		final byte[] buf = new byte[1024];
		
		int len = 0;
		int readed = 0;
		int left = 0;
		
		// kernel: page 1 <- n pages
		// page_size -> (n+1)*page_size
		left = size;
		
		final File of = new File(IMAGE_HOME+"\\"+name);
		
		if (of.exists()) of.delete();
		
		final FileOutputStream fos = new FileOutputStream(of, /*append*/true);
		
		while (left > 0)
		{
			len = left>1024?1024:left;
			int rl = dis.read(buf, 0, len);
			
			fos.write(buf, 0, rl);
			
			readed += rl;
			left -= rl;
		}
		
		System.out.println("*** dump "+readed+" of "+size+" to "+name);
		
		fos.close();
	}

}
